
package astudy.datasource.dbf;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.util.Calendar;

public class Writer {

    public static final byte DBF_FILE_EOF    = 0x1A;
    public static final byte DBF_HEAD_EOF    = 0x0D;

    private BufferedOutputStream stream;
    private long recCount;
    private Field fields[];
    private String fileName;
    public String charsetName = "GBK";

    public Writer(String s, Field ajdbfield[], boolean append) throws DBFException {

        stream = null;
        recCount = 0;
        fields = null;
        fileName = s;
        try {
        	//wong.tong
        	//check file exists or not
        	File file = new File(s);
            if(!file.exists()) {
				append = false;
            }
            FileOutputStream fos = new FileOutputStream(s, append);
            init(fos, ajdbfield, append);
        } catch (FileNotFoundException e) {
            throw new DBFException(e);
        }
    }

    public Writer(OutputStream outputstream, Field ajdbfield[], boolean append) throws DBFException {

        stream = null;
        recCount = 0;
        fields = null;
        fileName = null;
        init(outputstream, ajdbfield, append);
    }
    
    private long _getRecordCount(byte[] b) {
    	
    	long count  = 0;
        count += (b[0] & 0xFF);
        count += (b[1] & 0xFF) << 8;
        count += (b[2] & 0xFF) << 16;
        count += (b[3] & 0xFF) << 24;
        return count;
    }
    
    private void init(OutputStream outputstream, Field ajdbfield[], boolean append) throws DBFException {

        fields = ajdbfield;
        try {
            stream = new BufferedOutputStream(outputstream);
            if(append && fileName != null) {
                RandomAccessFile randomAccessFile = new RandomAccessFile(fileName, "rw");
                if(randomAccessFile.length() >= 33) {  //DBF Head Min Length
                    //����ԭDBF�ļ��ĳ���
                    randomAccessFile.seek(4L);
                    byte countByteArray[] = new byte[4];
                    randomAccessFile.read(countByteArray);
                    recCount  = _getRecordCount(countByteArray);

                    //ɾ��ԭDBF�ļ����һ���ļ���ֹ��(DBFWriter.DBF_FILE_EOF = 0x0D)
                    long fileLength = randomAccessFile.length();
                    randomAccessFile.seek(fileLength - 1);
                    if(randomAccessFile.readByte() == Writer.DBF_FILE_EOF) {
                        randomAccessFile.setLength(fileLength - 1);
                    }
                } else {
                    append = false;
                }
                randomAccessFile.close();
            }
            if(!append) {
                writeHeader();
                for(int i = 0; i < ajdbfield.length; i++)
                    writeFieldHeader(ajdbfield[i]);
                stream.write(Writer.DBF_HEAD_EOF);
                stream.flush();
            }
        } catch (Exception exception) {
            throw new DBFException(exception);
        }
    }

    private void writeHeader() throws IOException {

        byte abyte0[] = new byte[16];
        abyte0[0] = 3;
        Calendar calendar = Calendar.getInstance();
        abyte0[1] = (byte)(calendar.get(Calendar.YEAR) - 1900);
        abyte0[2] = (byte)(calendar.get(Calendar.MONTH) + 1);
        abyte0[3] = (byte)calendar.get(Calendar.DAY_OF_MONTH);
        abyte0[4] = 0;
        abyte0[5] = 0;
        abyte0[6] = 0;
        abyte0[7] = 0;
        int i = (fields.length + 1) * 32 + 1;
        abyte0[8] = (byte)(i % 256);
        abyte0[9] = (byte)(i / 256);
        int j = 1;
        for(int k = 0; k < fields.length; k++) {
			j += fields[k].getLength();
        }

        abyte0[10] = (byte)(j % 256);
        abyte0[11] = (byte)(j / 256);
        abyte0[12] = 0;
        abyte0[13] = 0;
        abyte0[14] = 0;
        abyte0[15] = 0;
        stream.write(abyte0, 0, abyte0.length);
        for(int l = 0; l < 16; l++) {
			abyte0[l] = 0;
        }
        stream.write(abyte0, 0, abyte0.length);
    }

    private void writeFieldHeader(Field jdbfield) throws IOException {

        byte abyte0[] = new byte[16];
/*
        String s = jdbfield.getName();
        int i = s.length();
        if(i > 10)
            i = 10;
        for(int j = 0; j < i; j++)
            abyte0[j] = (byte)s.charAt(j);
*/
        //zxd modify brgin ###
        byte[] b = jdbfield.getName().getBytes(charsetName);
        int i = b.length;
        if(i > 10) {
			i = 10;
        }
            
        for(int j = 0; j < i; j++) {
			abyte0[j] = b[j];
        }
            
        //zxd modify end ###
        
        for(int k = i; k <= 10; k++) {
			abyte0[k] = 0;
        }
            

        abyte0[11] = (byte)jdbfield.getType();
        abyte0[12] = 0;
        abyte0[13] = 0;
        abyte0[14] = 0;
        abyte0[15] = 0;
        stream.write(abyte0, 0, abyte0.length);
        for(int l = 0; l < 16; l++) {
			abyte0[l] = 0;
        }

        abyte0[0] = (byte)jdbfield.getLength();
        abyte0[1] = (byte)jdbfield.getDecimalCount();
        stream.write(abyte0, 0, abyte0.length);
    }

    public void addRecord(Object aobj[]) throws DBFException, UnsupportedEncodingException {

        if(aobj.length != fields.length) {
			throw new DBFException("Error adding record: Wrong number of values. Expected " + fields.length + ", got " + aobj.length + ".");
        }
            
        int i = 0;
        for(int j = 0; j < fields.length; j++) {
			i += fields[j].getLength();
        }

        byte abyte0[] = new byte[i];
        int k = 0;
        for(int l = 0; l < fields.length; l++) {
/*
            String s = fields[l].format(aobj[l]);
            for(int i1 = 0; i1 < fields[l].getLength(); i1++)
                abyte0[k + i1] = (byte)s.charAt(i1);
*/
            //zxd modify begin ###
            byte[] b= fields[l].format(aobj[l]).getBytes(charsetName);
            for(int i1 = 0; i1 < b.length && i1 < fields[l].getLength(); i1++)
                abyte0[k + i1] = b[i1];
            //zxd modify end ###

            k += fields[l].getLength();
        }

        try {
            stream.write(32);
            stream.write(abyte0, 0, abyte0.length);
            stream.flush();
        } catch (IOException ioexception) {
            throw new DBFException(ioexception);
        }
        recCount++;
    }

    public void close() throws DBFException {

        try {
            stream.write(Writer.DBF_FILE_EOF);
            stream.close();
            if (fileName != null) {
                RandomAccessFile randomaccessfile = new RandomAccessFile(fileName, "rw");
                randomaccessfile.seek(4L);
                byte abyte0[] = new byte[4];
                abyte0[0] = (byte)(recCount % 256);
                abyte0[1] = (byte)((recCount / 256) % 256);
                abyte0[2] = (byte)((recCount / 0x10000) % 256);
                abyte0[3] = (byte)((recCount / 0x1000000) % 256);
                randomaccessfile.write(abyte0, 0, abyte0.length);
                randomaccessfile.close();
            }
        } catch (IOException ioexception) {
            throw new DBFException(ioexception);
        }
    }
}
